/*
 * Copyright (c) 2023 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include <mc/log.h>

#include "interface/optical_module/cooling.h"

namespace dev {

bool OpticalModule_Cooling::get_om_temp_by_ncsi(uint8_t port_id) {
    uint16_t om_temp = 0;
    bool     ret     = m_ncsi_over_mctp_huawei->get_om_temp(DEFAULT_PACKAGE_ID, port_id, &om_temp);
    if (!ret) {
        elog("Error: Failed to get om temp");
        return false;
    }
    double temperature = (double)(int8_t)om_temp;

    uint16_t om_temp_data = om_temp & OM_TEMP_DATA_BIT;

    if (om_temp_data == OM_TEMP_INVALID || om_temp_data == OM_TEMP_NA || om_temp_data == OM_TEMP_ERROR) {
        temperature = 0xFFFF;
    }
    TemperatureCelsius.set_value(temperature);
    return true;
}

bool OpticalModule_Cooling::get_optical_module_info_by_ncsi(uint8_t port_id) {
    ncsi_optical_module_info_t optical_module_info;
    bool ret = m_ncsi_over_mctp_huawei->get_optical_module_info(DEFAULT_PACKAGE_ID, port_id,
                                                                &optical_module_info);
    if (!ret) {
        elog("Error: Failed to get optical module info");
        return false;
    }
    if (optical_module_info.medium_type == "Optical" ||
        optical_module_info.medium_type == "Aoc" ||
        optical_module_info.medium_type == "Electric") {
        TemperatureLowerThresholdCritical.set_value(
            optical_module_info.temperature_lower_threshold_critical);
        TemperatureUpperThresholdCritical.set_value(
            optical_module_info.temperature_upper_threshold_critical);
        return true;
    }
    return false;
}

void OpticalModule_Cooling::start_ncsi_update_task_huawei(ncsi_over_mctp_hw_ptr ncsi_over_mctp,
                                                          mc::milliseconds      interval,
                                                          uint8_t               port_id) {
    m_ncsi_over_mctp_huawei = ncsi_over_mctp;
    m_ncsi_timer            = new mc::core::timer(this);
    m_ncsi_timer->timeout.connect([this, port_id]() {
        get_om_temp_by_ncsi(port_id);
        get_optical_module_info_by_ncsi(port_id);
    });
    m_ncsi_timer->set_single_shot(false);
    m_ncsi_timer->start(interval);
}

void OpticalModule_Cooling::stop_ncsi_update_task() {
    if (m_ncsi_timer) {
        m_ncsi_timer->stop();
        delete m_ncsi_timer;
        m_ncsi_timer = nullptr;
    }
    if (m_ncsi_over_mctp_huawei) {
        m_ncsi_over_mctp_huawei = nullptr;
    }
}

//  ==== smbus ====
void OpticalModule_Cooling::update_om_temp_by_smbus(uint8_t port_id) {
    // 检查网卡健康状态, 无需进行数据更新，如果Health不为0，则需要报出错误码
    // 使用静态变量缓存，减少频繁创建
    static mc::mutable_dict request_template{{"opcode", 0x0400}};
    auto rsp = m_smbus_obj->send_request(request_template);
    if (rsp.empty()) {
        elog("Error: Failed to check netCard Health");
        return;
    }
    std::vector<uint16_t> om_temp_array;
    try {
        for (size_t i = 0; i < rsp.size(); i += 2) {
            uint16_t om_temp = static_cast<uint16_t>((static_cast<uint16_t>(rsp[i]) & 0xFF) |
                                                     (static_cast<uint16_t>(rsp[i + 1]) << 8));
            om_temp_array.push_back(om_temp);
        }
    } catch (const std::exception& e) {
        elog("Error: Failed to parse om temp by smbus, exception: ${exception}",
             ("exception", e.what()));
        return;
    }
    uint16_t result = om_temp_array[port_id];
    if (result == 0x7FFD) {
        dlog("optical temp for port ${port_id} invalid", ("port_id", port_id));
        TemperatureCelsius.set_value(0xFFFF);
        return;
    } else if (result == 0x7FFE) {
        dlog("optical temp for port ${port_id} not available", ("port_id", port_id));
        TemperatureCelsius.set_value(0xFFFF);
        return;
    } else if (result == 0x7FFF) {
        dlog("Failed to read optical temp for port ${port_id}", ("port_id", port_id));
        TemperatureCelsius.set_value(0xFFFF);
        return;
    } else {
        TemperatureCelsius.set_value(result);
        return;
    }
}

void OpticalModule_Cooling::start_smbus_update_task(smbus_obj_ptr    smbus_obj,
                                                    mc::milliseconds interval, uint8_t port_id) {
    if (!smbus_obj) {
        elog("Error: smbus_obj is not initialized");
        return;
    }
    // 保存传入的smbus_obj到成员变量
    m_smbus_obj = smbus_obj;

    m_smbus_timer = new mc::core::timer(this);
    m_smbus_timer->timeout.connect([this, port_id]() {
        update_om_temp_by_smbus(port_id);
    });
    m_smbus_timer->set_single_shot(false);
    m_smbus_timer->start(interval);
}

void OpticalModule_Cooling::stop_smbus_update_task() {
    if (m_smbus_timer) {
        m_smbus_timer->stop();
        delete m_smbus_timer;
        m_smbus_timer = nullptr;
    }
}

} // namespace dev

MC_REFLECT(dev::OpticalModule_Cooling)